/**
 * 
 */
package juf;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;


/**Data source for accessing a method or method-pair on an object and exposing
 * it using the Property interface
 * 
 * @author Dale Halliwell, Parul Sharma
 *
 */
public class MethodProperty implements Property {

	private Method getter = null; //accessors
	private Method setter = null;
	
	private Object pojo; //the data source object
	
	/**Create a MethodProperty  from a getter-setter method pair
	 * @param getter method to read the data from
	 * @param setter method to set the data
	 * @param pojo object to provide the method
	 */
	public MethodProperty (Method getter, Method setter, Object pojo){
		this(getter, pojo);		//must be first line
		this.setter = setter;

		//verify that the trimmed names of the getter and setter match names & types
		String getterTrimmed = trimName(getter);
		String setterTrimmed = trimName(setter);
		if (!getterTrimmed.equals(setterTrimmed)){
			//TODO: handle the exception?
		}
	}	
	

	/**Create a read-only MethodProperty  from a single getter method
	 * @param getter method to read the data from
	 * @param pojo object to provide the method
	 */
	public MethodProperty (Method getter, Object pojo){
		this.getter = getter;
		this.pojo = pojo;	

	}
	
	public static int getCapitalIndex(String string){
		int index = 0;
		for (int i=0; i<string.length(); i++){
			if (Character.isUpperCase(string.charAt(i))){
				index = i;	
				break;
			}
		}
		return index;
	}
	
	//return true if this is a getter - starts with is or get 
	//and has a non void return type
	public static boolean isGetter(Method m){
		
		//checks when the capital letter starts 
		//because anything before the capital letter
		//qualifies as a getter command like 'get' or 'is'
		String methodName = m.getName();
		int index = getCapitalIndex(methodName);
		
		if (methodName == "getClass") //inherits from Object
			return false;
		
		String getterType = "";
		if (index > 0)
			getterType = methodName.substring(0, index); 
		return ((getterType.equals("get") || getterType.equals("is")) && !m.getReturnType().equals(void.class)); 
	}
	
	//return true if this is a setter - starts with set and takes 1 parameter?
	public static boolean isSetter(Method m){
		
		//checks when the capital letter starts 
		//because anything before the capital letter
		//qualifies as a getter command like 'get' or 'is'
		String methodName = m.getName();
		int index = getCapitalIndex(methodName);
		
		String setterType = "";
		if (index > 0)
			setterType = methodName.substring(0, index); 
		
		return (setterType.equals("set")); 
	}
	
	
	//trims the "get" "set" "is" etc off a method name
	//that is it trims everything before the first capital letter encountered
	//eg in getFirstName get is trimmed and in isIsland is is trimmed
	public static String trimName(Method m){
		String s = m.getName();
		int index = getCapitalIndex(s);
		String result = "";
		if (index > 0)
			result = s.substring(index); 
		else{
			//exception TODO
			//it seems there are always going to be some method names
			//that pass through here like "wait" "notify" etc
			//which is why when I tried to throw a custom exception inside this 
			//else though the form loaded fine I saw that exception
			//in the console - is this desirable?
		}
		return result;
		
	}
	
	/* (non-Javadoc)
	 * @see juf.Property#getName()
	 */
	@Override
	public String getName() {
		return trimName(getter);
	}

	/* (non-Javadoc)
	 * @see juf.Property#getType()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Class getType() {
		return getter.getReturnType();
	}

	/* (non-Javadoc)
	 * @see juf.Property#getValue()
	 */
	@Override
	public Object getValue() {
		try {
			return getter.invoke(pojo);
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see juf.Property#isReadOnly()
	 */
	@Override
	public boolean isReadOnly() {
		return (setter == null);
	}

	/* (non-Javadoc)
	 * @see juf.Property#setValue(java.lang.Object)
	 */
	@Override
	public void setValue(Object value) {
		if (!isReadOnly()){
			try {
				setter.invoke(pojo,value);
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	/* (non-Javadoc)
	 * @see juf.Property#getAnnotations()
	 */
	@Override
	public Annotation[] getAnnotations(){
				
		return getter.getAnnotations();
		
	}

	
}
